Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Packed, typed object ids #229

Merged
merged 8 commits into from
Aug 20, 2019
Merged

Packed, typed object ids #229

merged 8 commits into from
Aug 20, 2019

Conversation

daboross
Copy link
Collaborator

Resolves #51, resolve #192.

This is based on an initial implementation @Dessix made, but I removed some of the optimizations in favor of simpler code, and created a separate untyped version.

This implements the binary/packed 12-byte storage described in #192 for object ids, and implements fairly efficient transfers of these 12-byte values between rust and JS. All of the translations, formatting and parsing are unit tested for reliability (since there's no reliance on a screeps server for that)

This also implements a typed version of the id, ObjectId<T>. The type is purely for type inference and optional safety - it has a into_type() method to change the inner type arbitrarily, and can be freely transformed into and from untyped object ids. My thought was that allowing this kind of "free" transmutation would mean we can have serialization and deserialization which just works, and doesn't have to do anything like calling into JS and checking Game.getObjectById() to ensure it's the right type. It means we need that one technically uneccessary check if you get an ID from a thing and then immediately use get_object_typed during the same tick, but I think the advantages are worth it.

Copy link
Collaborator

@ASalvail ASalvail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be nice to implement a few more Into trait for `ObjectId:

  • ArrayString
  • String
  • T
    ... and From
  • [u32;3]
  • ArrayString
  • String

It'd make using ObjectId much more convivial.

You might argue this is best left for another PR though.

/// ```
pub fn from_packed_js_val(packed_val: Reference) -> Result<Self, ConversionError> {
let mut packed = [0u32; 3];
// TODO: make this more efficient, once we get mutable UnsafeTypedArrays.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind switching this to a tracking issue? We might never see it again otherwise :P
(Feel free to leave the todo)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #230.

src/objects.rs Outdated
js_unwrap!(@{self.as_ref()}.id)
/// Retrieves this object's id as an untyped, packed value.
///
/// This has no major differences from [`HasId::id`] besides the return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

besides -> beside?

Copy link
Collaborator Author

@daboross daboross Aug 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right - I think I almost never use besides correctly...

Edit: looking stuff online seems to come up with conflicting information. I think I'll just switch to "except for" for clarity.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meh, I'm not married to either, hence the question mark. Feel free to leave it like that.

@daboross
Copy link
Collaborator Author

With the other conversions - I'm happy to add Into<ArrayString<[u8; 24]>>, Into<String> and From<[u32; 3]> - those seem natural.

I think From<String> would be a bit of a stretch though - conversion into ObjectId could definitely fail if it isn't in the right format, and we do have a FromStr implementation which allows using string.parse()??

@ASalvail
Copy link
Collaborator

You're right. Maybe an expensive TryFrom<String>, but that, I'd definitely leave for later.

@daboross
Copy link
Collaborator Author

daboross commented Aug 20, 2019

Sounds good!

I forgot you mentioned Into<T> too, and I didn't address that. What would you think of adding that as a method, like try_resolve(&self) -> Result<Option<T>, ConversionError> - maybe with a counterpart resolve(&self) -> Option<T> which panics on incorrect type?

There was a similar try_resolve method in @Dessix's original implementation, but I didn't include it since I was thinking of game::get_object_typed. But it could definitely be put in as a convenience for that function..

@Dessix
Copy link
Contributor

Dessix commented Aug 20, 2019

I think a combination of try_resolve and resolve sounds good, but I may be biased, as- indeed- I did have that functionality in my own implementation.

There is also potential room for a threadlocal LRU which is per-tick reset caching objects by ID to references, reducing calls out to JS.

@ASalvail
Copy link
Collaborator

Why not TryInto? I don't get why we don't rely on those here (except for the unsafe stuff) as it seems to be the most natural given the rest of the codebase.

Otherwise, I'm certainly not against resolve and try_resolve.

@daboross
Copy link
Collaborator Author

daboross commented Aug 20, 2019

I'd argue against it mostly because it doesn't feel like an into operation - an ID is a fairly separate bit of information from the object it names, and "into" conversions usually mean turning the same bit of data into another form? Like we're getting a lot more than another form of the information contained in ObjectId.

The other reason would be ease of use, since we would be implementing TryInto<Option<T>>. id.resolve() seems like it would be nicer to use (and read) than let x: Option<_> = id.try_into(); or Option::try_from(id). Since the id has type info, a method should be able to work without any annotations, whereas TryFrom / TryInto will always need to rely on type inference.

@ASalvail
Copy link
Collaborator

Alright, you make a good case, I agree with you!

Copy link
Collaborator

@ASalvail ASalvail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the additions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Store object ids as binary representation RoomObjectId type wrapper
3 participants